package jeconbond.automata.behaviour;

import java.util.ArrayList;
import java.util.List;

import jeconbond.automata.INode;
import jeconbond.automata.IOutcome;
import jeconbond.automata.behaviour.IWalkStrategy;
import jeconbond.economic.production.IResourceNecessityInformer;
import jeconbond.economic.resources.ResourceBasket;
import jeconbond.economic.resources.ResourceStorrage;
import jeconbond.economic.resources.ResourceUtils;

public class ListPlanWalker implements IWalkStrategy, IResourceNecessityInformer {
	private List<IOutcome> plan;
	private int position;
	private int length;
	private boolean checkAbility;
	private int infoTraceDepth;
	
	private ResourceStorrage inputRS;
	private ResourceStorrage outputRS;

	public ListPlanWalker(
			List<IOutcome> plan, 
			ResourceStorrage inputRS,
			ResourceStorrage outputRS,
			int position, 
			int infoTraceDepth, 
			boolean checkAbility
	) {
		this.plan = plan;
		this.checkAbility = checkAbility;
		this.position = position;
		this.infoTraceDepth = infoTraceDepth;
		
		this.inputRS = inputRS;
		this.outputRS = outputRS;
		
		length = plan.size();
	}

	public ListPlanWalker(
			List<IOutcome> plan,
			ResourceStorrage inputRS,
			ResourceStorrage outputRS
	) {
		this(plan, inputRS, outputRS, 0, plan.size(), true);
	}

	@Override
	public IOutcome doWalk(INode state) {
		IOutcome out = plan.get(position);
		boolean canWalk = ResourceUtils.checkIfResourcesEnought_unsafe(inputRS, out);
		if (!canWalk) {
			return null;
		}
		ResourceUtils.processOut(inputRS, outputRS, out);
		position++;
		if (position == length) {
			position = 0;
		}
		if (checkAbility) {
			boolean outExists = false;
			for (IOutcome ouCand : state.getOutcomes()) {
				if (ouCand == out) {
					outExists = true;
				}
			}
			if (!outExists) {
				throw new BehaviourExceprion("Can't find actual outcome.");
			}
		}
		return out;
	}
	
	static { System.out.println("\nPERFORMANCE_FUTURE very ListPlanWalker.listInfoItems() can be prepared in constructor."); }

	public List<InfoItem> listInfoItems() {
		List<InfoItem> result = new ArrayList<InfoItem>();
		for (int i = 0; i < infoTraceDepth; i++) {
			int pos = (position + i) % length;
			IOutcome out = plan.get(pos);
			for (ResourceBasket rb : out.getRequirements()) {
				result.add(new InfoItem(rb, i));
			}
		}
		return result;
	}

	public List<IOutcome> getPlan() {
		return plan;
	}
	
	public void setPlan(List<IOutcome> plan) {
		this.plan = plan;
		refreshPlan(position, plan.size());
	}

	public void refreshPlan(int position, int infoTraceDepth) {
		this.position = position;
		this.length = plan.size();
		this.infoTraceDepth = infoTraceDepth; 
	}
}
